/*
 * Copyright (c) 2006-2020, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-10-20     xu           first version
 */

#include "board.h"
#include "rtconfig.h"
#include "stm32h7xx_hal_mmc.h"

#define DBG_TAG "drv.emmc"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <rtthread.h>

#define RETRY_TICK_INC 10
#define EMMC_MAX_RETRY 10
#define EMMC_MIN_RW_BLOCKS 8uL
#define EMMC_DEFAULT_TIMEOUT (12000)
#define EMMC_BLOCK_NAME "emmc"

#ifdef BSP_USING_EMMC

static struct rt_device emmc_device;
// static struct rt_semaphore tx_sem;
// static void MMC_OnTxComplete(MMC_HandleTypeDef *hmmc)
// {
//     rt_sem_release(&tx_sem);
//     rt_kprintf("%s: work!\n", __func__);
// }

#define HEXDUMP_WIDTH 16
static void hexdump(const void* buffer, long size)
{
    long i, j;
    const rt_uint8_t* data = (const rt_uint8_t*)buffer;
    rt_kprintf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n");
    for (i = 0; i < size; i += HEXDUMP_WIDTH) {
        rt_kprintf("[%08x] ", i);
        /* dump hex */
        for (j = 0; j < HEXDUMP_WIDTH; j++) {
            if (i + j < size) {
                rt_kprintf("%02X ", data[i + j]);
            } else {
                rt_kprintf("   ");
            }
        }
        rt_kprintf("\n");
    }
    rt_kprintf("\n");
}

static rt_err_t rtt_emmc_init(rt_device_t dev)
{
    LOG_D("%s: DONE!", __func__);
    return RT_EOK;
}

extern void MX_SDMMC1_MMC_Init(void);
extern void MX_SDMMC1_MMC_DeInit(void);


static rt_err_t rtt_emmc_open(rt_device_t dev, rt_uint16_t oflag)
{
    // RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
    // MMC_HandleTypeDef* hmmc = (MMC_HandleTypeDef*)dev->user_data;

    // rt_sem_init(&tx_sem, "mmc_tx", 0, RT_IPC_FLAG_FIFO);
    // HAL_MMC_RegisterCallback(hmmc, HAL_MMC_TX_CPLT_CB_ID, MMC_OnTxComplete);
    MX_SDMMC1_MMC_Init();
    LOG_D("%s: DONE!", __func__);
    return RT_EOK;
}

static rt_err_t rtt_emmc_close(rt_device_t dev)
{
    MX_SDMMC1_MMC_DeInit();
    LOG_D("%s: DONE!", __func__);
    return RT_EOK;
}

static void sys_delay(uint32_t n)
{
    uint32_t targetTick = SysTick->VAL + n;
    while (SysTick->VAL < targetTick) {}
}

static rt_size_t rtt_emmc_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
{
    HAL_StatusTypeDef ret = 0;
    RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
    RT_ASSERT(buffer != RT_NULL);

    rt_enter_critical();
    MMC_HandleTypeDef* hmmc = (MMC_HandleTypeDef*)dev->user_data;
    RT_ASSERT(HAL_MMC_GetState(hmmc) == HAL_MMC_STATE_READY);
    ret = HAL_MMC_ReadBlocks(hmmc, (uint8_t*) buffer, pos, size, EMMC_DEFAULT_TIMEOUT);
    // while (HAL_MMC_GetCardState(hmmc) != HAL_MMC_CARD_TRANSFER) {}

    uint32_t retry = 0;
    while (ret != HAL_OK && retry < EMMC_MAX_RETRY) {
        retry++;
        MX_SDMMC1_MMC_DeInit();
        sys_delay(retry * RETRY_TICK_INC);
        MX_SDMMC1_MMC_Init();
        ret = HAL_MMC_ReadBlocks(hmmc, (uint8_t*) buffer, pos, size, EMMC_DEFAULT_TIMEOUT);
        // while (HAL_MMC_GetCardState(hmmc) != HAL_MMC_CARD_TRANSFER) {}
    }
    rt_exit_critical();

    if (ret != HAL_OK) {
        LOG_E("%s: MMC read failed, pos=%d, size=%d, error=0x%X!\n", __func__, pos, size, HAL_MMC_GetError(hmmc));
    }


    return size;
}

static rt_size_t rtt_emmc_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
{
    HAL_StatusTypeDef ret = 0;
    RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);
    RT_ASSERT(buffer != RT_NULL);

    rt_enter_critical();
    MMC_HandleTypeDef* hmmc = (MMC_HandleTypeDef*)dev->user_data;
    RT_ASSERT(HAL_MMC_GetState(hmmc) == HAL_MMC_STATE_READY);
    ret = HAL_MMC_WriteBlocks(hmmc, (uint8_t*) buffer, pos, size, EMMC_DEFAULT_TIMEOUT);

    uint32_t retry = 0;
    while (ret != HAL_OK && retry < EMMC_MAX_RETRY) {
        retry++;
        MX_SDMMC1_MMC_DeInit();
        sys_delay(RETRY_TICK_INC * (1 << retry));
        MX_SDMMC1_MMC_Init();
        ret = HAL_MMC_WriteBlocks(hmmc, (uint8_t*) buffer, pos, size, EMMC_DEFAULT_TIMEOUT);
    }
    rt_exit_critical();

    if (ret != HAL_OK) {
        LOG_E("%s: MMC write failed, pos=%d, size=%d, error=0x%X!", __func__, pos, size, HAL_MMC_GetError(hmmc));
        return -1;
    }

    return size;
}

static rt_err_t rtt_emmc_control(rt_device_t dev, int cmd, void* args)
{
    RT_ASSERT(dev != RT_NULL && dev->user_data != RT_NULL);

    if (cmd == RT_DEVICE_CTRL_BLK_GETGEOME) {
        RT_ASSERT(args != RT_NULL);

        HAL_MMC_CardInfoTypeDef cardInfo = {0};
        MMC_HandleTypeDef* hmmc = (MMC_HandleTypeDef*)dev->user_data;
        if (HAL_MMC_GetCardInfo(hmmc, &cardInfo) != HAL_OK) {
            LOG_E("%s: HAL_MMC_GetCardInfo FAIL!\n", __func__);
            return RT_EIO;
        }

        struct rt_device_blk_geometry* geometry;
        geometry = (struct rt_device_blk_geometry*)args;
        geometry->bytes_per_sector = cardInfo.BlockSize;
        geometry->block_size = cardInfo.BlockSize;
        geometry->sector_count = 2048 * 256; // 2048 sectors means 1MB
        // geometry->sector_count = cardInfo.BlockNbr;
        LOG_I("block_size:%d, sector_size:%d, sector_count:%d", geometry->block_size, geometry->bytes_per_sector,
              geometry->sector_count);
    } else if (cmd == RT_DEVICE_CTRL_BLK_SYNC) {
        LOG_W("%s: cmd=%d RT_DEVICE_CTRL_BLK_SYNC, nothing todo!", __func__, cmd);
        sys_delay(1);
    } else {
        LOG_W("%s: cmd=%d not support!", __func__, cmd);
    }

    return RT_EOK;
}

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops emmc_ops = {.init = rtt_emmc_init,
                                              .open = rtt_emmc_open,
                                              .close = rtt_emmc_close,
                                              .read = rtt_emmc_read,
                                              .write = rtt_emmc_write,
                                              .control = rtt_emmc_control};
#endif /* RT_USING_DEVICE_OPS */

int rt_hw_emmc_init(void)
{
    rt_err_t ret = RT_EOK;

    /* 设备类型为块设备类型 */
    emmc_device.type = RT_Device_Class_Block;
    emmc_device.rx_indicate = RT_NULL;
    emmc_device.tx_complete = RT_NULL;

#ifdef RT_USING_DEVICE_OPS
    emmc_device.ops = &emmc_ops;
#else  /* RT_USING_DEVICE_OPS */
    emmc_device.init = rtt_emmc_init;
    emmc_device.open = rtt_emmc_open;
    emmc_device.close = rtt_emmc_close;
    emmc_device.read = rtt_emmc_read;
    emmc_device.write = rtt_emmc_write;
    emmc_device.control = rtt_emmc_control;
#endif /* not define RT_USING_DEVICE_OPS */

    extern MMC_HandleTypeDef hmmc1;
    emmc_device.user_data = &hmmc1;
    LOG_I("MMC handle: %p", emmc_device.user_data);

    /* register a character device */
    return rt_device_register(&emmc_device, EMMC_BLOCK_NAME, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
}
INIT_DEVICE_EXPORT(rt_hw_emmc_init);
#endif /* BSP_USING_EMMC */
